/**
 * class:
 * 类的内部定义的所有方法都是不可枚举的（ non-enumerable)
 * 类和模块的内部默认使用严格模式
 * new 命令生成对象实例时自动调用constructor方法（一个类必须要有）。
 * constructor 默认返回的是实例对象（this）
 */
class Point {
  // @readonly
  name = 'lisa'

  constructor(x, y) {
    this.x = x
    this.y = y
  }

  // 该方法在原型对象身上，供所有实例对象使用
  draw() {
    console.log('draw')
  }

  // 赋值语句会将这个方法放在实例对象自身身上
  say = () => {
    console.log('say hello ')
  }
}

let p1 = new Point(2, 3)
p1.draw() //draw

// ES6 写法是不可遍历的
Point.prototype.draw() // draw
let keys_ES6 = Object.keys(Point.prototype)
console.log(keys_ES6) // []

// ES5 写法是可遍历的
Point.prototype.toString = function () {
  console.log('toString')
}

let keys_ES5 = Object.keys(Point.prototype)
console.log(keys_ES5)  // [ 'toString' ]

// ES5 样，实例的属性除非显式定义在其本身（即 this 对象〉上，否则都是定义在原型（即 Class ）上。
// x,y 是定义在实例对象this上，而Draw是原型对象的属性
console.log(p1.hasOwnProperty('x'))  // true
console.log(p1.hasOwnProperty('y'))  // true
console.log(p1.hasOwnProperty('Draw'))  // false

// 类的所有实例对象共享一个原型对象
let p2 = new Point(3, 4)
console.log(p1.__proto__ === p2.__proto__) // true，他们的原型都是Point.prototype

// 生产中我们通过 Object.getPrototypeOf() 来获取实例对象的原型，再为原型添加方法属性
let p1_proto = Object.getPrototypeOf(p1)
let p2_proto = Object.getPrototypeOf(p2)
console.log(p1_proto === p2_proto)  // true

p1_proto.getPoints = function () {
  console.log('points:', this, x, this.y)
}

// 装饰器
console.log(p1.name)

/**
 * 私有方法：在方法前面加_（不保险，类外依然可以访问）、移出类外调用、symbol
 * 私有属性：ES6不支持私有属性
 * 读取器：get/set 对某个属性设置存值函数和取值函数，拦截该属性的存取行为。
 */

let bar = Symbol('bar')
let snaf = Symbol('snaf')

class People {
  name = ''

  constructor(name) {
    this.name = name
  }

  get birthday() {
    return `${this.name}:birthday is 2000/01/01 `
  }

  set name(value) {
    console.log(`set name: ${value}`)
  }

  // 静态方法
  static changeBirth() {
    console.log(`${this.name} changeBirth`) // 这个name是类的名称
  }

  // 公有方法
  foo(baz) {
    this[bar](baz)
  }

  // 私有方法
  [bar](baz) {
    return this[snaf] = baz
  }
}

// bar snaf 都是 Symbol 值，导致第三方无法获取到它们，因此达到了私有方法和私有属性的效果。

let stu = new People('xiaoming')
const s_birth = stu.birthday
console.log(s_birth)  // xiaoming:birthday is 2000/01/01

/**
 * 静态方法：static
 * static 关键字，就表示该方法不会被实例继承，而是直接通过类调用，称为“静态方法”
 * 静态属性：因为 ES6 明确规定， class 内部只有静态方法，没有静态属性（已有static的提案）
 */

People.changeBirth() // People changeBirth

People.props = 'props' // 相当于静态属性
console.log(People.props)




